package com.example.design.nioandnettty.practice.websocketLongCon;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.stream.ChunkedWriteHandler;
import org.springframework.stereotype.Component;

/**
 * @Description TODO
 * @date 2023/9/27 10:53
 * @Author liuzhihui
 * @Version 1.0
 */
@Component
public class WebsocketNettyServer {

    public WebsocketNettyServer() {
        new Thread(() -> {
            //bossGroup连接线程组，主要负责接受客户端连接，一般一个线程足矣
            EventLoopGroup bossGroup = new NioEventLoopGroup(1);
            //workerGroup工作线程组，主要负责网络IO读写
            EventLoopGroup workerGroup = new NioEventLoopGroup();
            try {

                //启动辅助类
                ServerBootstrap serverBootstrap = new ServerBootstrap();
                //bootstrap绑定两个线程组
                serverBootstrap.group(bossGroup, workerGroup);
                //设置通道为NioChannel
                serverBootstrap.channel(NioServerSocketChannel.class);
                //可以对入站\出站事件进行日志记录，从而方便我们进行问题排查。
                serverBootstrap.handler(new LoggingHandler(LogLevel.INFO));
                //设置自定义的通道初始化器，用于入站操作
                serverBootstrap.childHandler(
                        //创建一个通道测试对象(匿名对象)
                        new ChannelInitializer<SocketChannel>() {
                            //给管道设置处理器
                            @Override
                            protected void initChannel(SocketChannel socketChannel) throws Exception {
                                ChannelPipeline pipeline = socketChannel.pipeline();
                                //因为是基于http协议，使用http的编码和解码器
                                pipeline.addLast(new HttpServerCodec());
                                //http是以块方式写的，添加ChunkedWrite处理器
                                pipeline.addLast(new ChunkedWriteHandler());
                                //因为http的数据，在传输过程中是分段的，HttpObjectAggregator就是可以将多个段聚合起来
                                //这就是为什么，当浏览器发送大量数据时，就会发出多次http请求的原因
                                pipeline.addLast(new HttpObjectAggregator(8192));
                                //对于webSocket的数据是以帧的形式传递
                                //可以看到webSocketFrame,下面有六个子类
                                //浏览器请求时 ws://localhost:7000/hello 表示请求的url,使用的是ws webSocket协议
                                //WebSocketServerProtocolHandler核心功能将http协议升级为ws协议，保持长连接
                                pipeline.addLast(new WebSocketServerProtocolHandler("/hello"));
                                //自定义的handler，处理业务逻辑
                                pipeline.addLast(new WebSocketHandler());
                            }
                        });
                //启动服务器,本质是Java程序发起系统调用，然后内核底层起了一个处于监听状态的服务，生成一个文件描述符FD
                // sync同步执行, 会阻塞, 确保服务器在继续执行任何其他操作之前已经绑定到端口
                ChannelFuture channelFuture = serverBootstrap.bind(8888).sync();

                // channelFuture.channel() 获取表示TCP连接的Channel对象
                // closeFuture()：关闭该Channel的操作，这是一个异步操作，这意味着它不会立即完成
                //sync()：这是一个阻塞调用，它会等待上面的close操作完成。尽管Netty旨在实现非阻塞编程，但此处使用了阻塞调用
                // 以确保连接在继续执行任何其他操作之前已经关闭。
                channelFuture.channel().closeFuture().sync();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                bossGroup.shutdownGracefully();
                workerGroup.shutdownGracefully();
            }
        }).start();
    }
}
